
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Data;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Xml;


using Swaf.Config;
using Swaf.Container;
using Swaf.BizRule;
using Swaf.Logging;

namespace Swaf.DataAccess
{
	/// <summary>
	/// Summary description for ConnectionFactoryContainerNode.
	/// </summary>
	public class ConnectionFactoryContainerNode : INameAddressSupport, ICatalogItem
	{
		protected string m_driverClass;
		protected string m_connectionInfo;
		protected string m_uid;
		protected string m_pwd;
		protected string m_catalogName;
		protected IList m_hooks = new ArrayList();
		protected int m_retryCount=0;
        protected string m_encoding = "Unicode";

		public ConnectionFactoryContainerNode(Match mr, object extraInfo)
		{
			if(extraInfo != null && extraInfo is XmlElement)
			{
				XmlElement el = (XmlElement)extraInfo;
				initializeFromElement(el);
			}
		}

		public ConnectionFactoryContainerNode(XmlElement el, string catalogName)
		{
			m_catalogName =catalogName;
			initializeFromElement(el);
		}

		protected void processHooks(XmlElement el)
		{
			Debug.Assert(el.Name == "Hooks");
			foreach(XmlElement hookInfo in el.ChildNodes)
			{
				try
				{
					IDbConnectionHook hook = (IDbConnectionHook)Cmn.callCtor(hookInfo.GetAttribute("type"));
					m_hooks.Add(hook);
				}
				catch(Exception e)
				{
					//TODO: log an error that a hook couldn't be created/called.
				}
			}
		}

		protected void initializeFromElement(XmlElement el)
		{
			try 
			{
				XmlNodeList children = el.ChildNodes;
				foreach(XmlNode childNode in children)
				{
					if(!(childNode is XmlElement))
						continue;
					XmlElement childEl = (XmlElement)childNode;
					if(childEl.Name.CompareTo("DriverClass") == 0)
						m_driverClass = childEl.InnerText;
					else if(childEl.Name.CompareTo("ConnectionInfo") == 0)
						m_connectionInfo = Application.globalApp.resMgr.resolve(childEl.InnerText);
					else if(childEl.Name.CompareTo("Uid") == 0)
						m_uid = childEl.InnerText;
					else if(childEl.Name.CompareTo("Pwd") == 0)
						m_pwd = childEl.InnerText;
					else if(childEl.Name.CompareTo("Hooks") == 0)
						processHooks(childEl);
					else if (childEl.Name.CompareTo("ConnectionRetryCount") == 0)
					{
						try
						{
							m_retryCount=int.Parse(childEl.InnerText);
						}
						catch(Exception e)
						{
							Application.currentApp.Log.println(LoggingLevels.Medium,"Error parsing ConnectionRetryCount property of DbConnection.");
						}
					}
                    else if (childEl.Name.CompareTo("Encoding") == 0)
                    {
                        m_encoding = childEl.InnerText;
                    }
				}

				if(m_connectionInfo != null && m_driverClass != null)
				{
					if(m_uid != null)
						m_connectionInfo = m_connectionInfo.Replace("{Uid}", m_uid);
					if(m_pwd != null)
						m_connectionInfo = m_connectionInfo.Replace("{Pwd}", m_pwd);

					m_connectionInfo=Application.currentApp.resMgr.resolve(m_connectionInfo);
				}
				else
				{
					//TODO: Throw some kind of exception.
				}
			}
			catch(Cmn.ReflectionException)
			{
				//TODO: Throw some other kind of exception.
			}
			catch(Exception)
			{
				//TODO: Throw some other kind of exception.
			}
		}

		#region INameAddressSupport Members

		public void put(string nameAddress, object val)
		{
		}

		public void put(string nameAddress, object val, object extraInfo)
		{
		}

		public object get(string nameAddress, object oneArg)
		{
			return this.get(nameAddress, new object[]{oneArg});
		}

		public object get(string nameAddress)
		{
			return this.get(nameAddress, new object[]{});
		}

		public object get(string nameAddress, params object[] infoArgs)
		{
			IRunningRule rule = ((IRuleManagerEx)Application.currentApp.ruleMgr).CurrentThreadRunningRule;
			AdoNetConnectionItem dbItem = null;
			ITransactionManager t = null;
			TransactionSupport trans =  TransactionSupport.none;
			if(infoArgs != null && infoArgs.Length > 0 && infoArgs[0] is ITransactionManager)
			{
				t = (ITransactionManager)infoArgs[0];
				dbItem = (AdoNetConnectionItem)t.getConnection(m_connectionInfo);
			}
			if(dbItem == null && t == null && rule != null)
			{
				string transactionLevel = rule.decoratorValue("transaction");
				if(transactionLevel != null && transactionLevel.Length > 0)
				{
					trans = (TransactionSupport)Enum.Parse(typeof(TransactionSupport), transactionLevel, true);
					if(trans != TransactionSupport.none)
					{
						t = (ITransactionManager)rule["_transaction:" + rule.UniqueId.ToString()];
						if(t != null) //Could be null because trans == TransactionSupport.uses and there was no transaction
						{
							dbItem = (AdoNetConnectionItem)t.getConnection(m_connectionInfo);
							//db might be null, which means that this is the first time this connection string has been used in this transaction
							//and code below will create the db connection and add it into the transaction.
						}
					}
				}
			}
			if(dbItem == null)
			{
				int retries=0;
				IDbConnection db = null;
				//We need to run this code block at least once.  We will loop until either we have a successful
				// connection or run out of retries.  If we run out of retries, the last error is thrown.  All errors
				// are traced.
				while(retries <= m_retryCount )
				{
					try
					{
						db = (IDbConnection)Cmn.callCtor(m_driverClass,
							new Type[]{typeof(String)}, new Object[]{Application.globalApp.resMgr.resolve(m_connectionInfo)});
					}
					catch(Cmn.ReflectionException e)
					{
						throw new InvalidConnectionClassException(m_driverClass, Application.globalApp.resMgr.resolve(m_catalogName));
					}

					try
					{
						if(db != null)
						{
							db.Open();
							break;
						}
					}
					catch(Exception e)
					{
						//Increment the retry count first.
						++retries;
						//Write the error to the trace log.
						Application.currentApp.Trace.write(TraceLevels.Normal,"Error connecting to db connection in catalog {0}. Error was {1}", m_catalogName,e.ToString());
						//If we are out of retries, throw the error.
						if (retries > m_retryCount)
						{
							throw new InvalidConnectionInfoException(m_connectionInfo, m_catalogName, e);
						}
					}
				}
				try
				{
					foreach(IDbConnectionHook hook in m_hooks)
						hook.processOpen(db);
					dbItem = new AdoNetConnectionItem(db);
					if(t != null)
					{
						if(trans == TransactionSupport.requires || 
							trans == TransactionSupport.requiresNew || 
							trans == TransactionSupport.uses)
						{
							dbItem.beginTrans();
						}
						t.addConnection(m_connectionInfo, dbItem);
						dbItem.closeOnForceClose();
					}
				}
				catch(Exception e)
				{
					db.Close();
					throw new InvalidConnectionInfoException(m_connectionInfo, m_catalogName, e);
				}
			}
			else
				dbItem.addRef();

			dbItem.retryCount=m_retryCount;
            dbItem.encoding = m_encoding;
			return dbItem;
		}

		public bool isGetOnly {	get{return false;}}

		public XmlSerializationSupport SerializationSupport {get{return XmlSerializationSupport.None;}}

		public string Xml {get{return null;}}

		public string getXml(string options) {return null;}

		public bool containsField(String nameAddress) {return false;}

		#endregion
		public IEnumerator GetEnumerator() { return null; }

		#region ICatalogItem Members
		protected string m_expiration=null;
		protected string m_key=null;
		protected string m_searchPath=null;

		public string catalogName
		{
			get
			{
				return m_catalogName;
			}
			set
			{
				m_catalogName=value;
			}
		}

		public string expiration
		{
			get
			{
				return m_expiration;
			}
			set
			{
				m_expiration=value;
			}
		}

		public string key
		{
			get
			{
				return m_key;
			}
			set
			{
				m_key=value;
			}
		}

		public string searchPath
		{
			get
			{
				return m_searchPath;
			}
			set
			{
				m_searchPath=value;
			}
		}

		public bool isCacheable
		{
			get { return false; }
		}

		#endregion
	}
}
